package com.dbflow5.processor.definition;

import com.dbflow5.annotation.TypeConverter;
import com.dbflow5.processor.ClassNames;
import com.dbflow5.processor.ProcessorManager;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;

import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.MirroredTypesException;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import java.util.ArrayList;
import java.util.List;

/**
 * Description: Holds data about type converters in order to write them.
 */
public class TypeConverterDefinition {

    public ClassName className;
    public boolean isDefaultConverter;

    public TypeName modelTypeName;
    public TypeName dbTypeName;
    public List<TypeName> allowedSubTypes;

    public TypeConverterDefinition(TypeConverter typeConverter, ClassName className, TypeMirror typeMirror, ProcessorManager manager, boolean isDefaultConverter) {
        this.className = className;
        this.isDefaultConverter = isDefaultConverter;

        List<TypeName> allowedSubTypes = new ArrayList<>();
        if (typeConverter != null) {
            try {
                typeConverter.allowedSubtypes();
            } catch (MirroredTypesException e) {
                List<? extends TypeMirror> types = e.getTypeMirrors();
                types.forEach(it -> {
                    allowedSubTypes.add(TypeName.get(it));
                });
            }
        }
        this.allowedSubTypes = allowedSubTypes;

        Types types = manager.typeUtils;

        DeclaredType typeConverterSuper = null;
        DeclaredType typeConverterType = manager.typeUtils.getDeclaredType(manager.elements
                .getTypeElement(ClassNames.TYPE_CONVERTER.toString()));

        for (TypeMirror superType : types.directSupertypes(typeMirror)) {
            TypeMirror erasure = types.erasure(superType);
            if (types.isAssignable(erasure, typeConverterType) || erasure.toString() == typeConverterType.toString()) {
                typeConverterSuper = (DeclaredType) superType;
            }
        }

        if (typeConverterSuper != null) {
            List<? extends TypeMirror> typeArgs = typeConverterSuper.getTypeArguments();
            dbTypeName = ClassName.get(typeArgs.get(0));
            modelTypeName = ClassName.get(typeArgs.get(1));
        } else {
            dbTypeName = null;
            modelTypeName = null;
        }
    }
}
